//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace OpenLS.Chart.UI
{
    class CylinderGeometryFactory : MeshGeometryFactory
    {
        public CylinderGeometryFactory(BarDirection direction)
            : base(direction)
        { 
        }

        private static double degreeToRadian(double degrees)
        {
            return (degrees / 180.0) * Math.PI;
        }

        private Point3D getPosition(double t, double value)
        {
            Bounds categoryBounds = GetCategoryBounds(_bounds);
            double category = categoryBounds.Mid + categoryBounds.Delta / 2 * Math.Cos(t);
            double z = _bounds.Z.Mid + _bounds.Z.Delta  / 2 * Math.Sin(t);
            return CreatePoint(value, category, z);
        }

        private Vector3D GetNormal(double t)
        {
            double x = Math.Cos(t);
            double z = Math.Sin(t);
            switch (Direction)
            {
                case BarDirection.Column:


                    return new Vector3D(x, 0, z);
                default:
                    return new Vector3D(0, x, z);
            }
        }

        private static Point GetTextureCoordinate(double t, double y)
        {
            Matrix m = new Matrix();
            m.Scale(1 / (2 * Math.PI), -0.5);

            Point p = new Point(t, y);
            p = p * m;

            return p;
        }

        private Bounds3D _bounds;

        public override MeshGeometry3D CreateGeometry(Bounds3D bounds, Bounds totalValueBounds2, double floor)
        {
            _bounds = bounds;
            int tDiv = 32;
            int yDiv = 1;
            double maxTheta = degreeToRadian(360.0);
            //double minY = -1.0;
            //double maxY = 1.0;

            double dt = maxTheta / tDiv;
            double dvalue = (GetValueBounds(bounds).Delta) / yDiv;

            MeshGeometry3D mesh = new MeshGeometry3D();

            for (int yi = 0; yi <= yDiv; yi++)
            {
                double value = GetValueBounds(bounds).Low + yi * dvalue;

                for (int ti = 0; ti <= tDiv; ti++)
                {
                    double t = ti * dt;

                    mesh.Positions.Add(getPosition(t, value));
                    mesh.Normals.Add(GetNormal(t));
                    mesh.TextureCoordinates.Add(GetTextureCoordinate(t, value));
                }
            }

            {
                double value = GetValueBounds(bounds).Low;
                for (int ti = 0; ti <= tDiv; ti++)
                {
                    double t = ti*dt;

                    mesh.Positions.Add(getPosition(t, value));
                    mesh.Normals.Add(GetNormal(t));
                    mesh.TextureCoordinates.Add(GetTextureCoordinate(t, value));
                }
            }


            for (int yi = 0; yi < yDiv; yi++)
            {
                for (int ti = 0; ti < tDiv; ti++)
                {
                    int x0 = ti;
                    int x1 = (ti + 1);
                    int y0 = yi * (tDiv + 1);
                    int y1 = (yi + 1) * (tDiv + 1);
                    if (Direction == BarDirection.Column)
                    {
                        mesh.TriangleIndices.Add(x0 + y0);
                        mesh.TriangleIndices.Add(x0 + y1);
                        mesh.TriangleIndices.Add(x1 + y0);

                        mesh.TriangleIndices.Add(x1 + y0);
                        mesh.TriangleIndices.Add(x0 + y1);
                        mesh.TriangleIndices.Add(x1 + y1);
                    }
                    else
                    {
                        mesh.TriangleIndices.Add(x0 + y0);
                        mesh.TriangleIndices.Add(x1 + y0);
                        mesh.TriangleIndices.Add(x0 + y1);

                        mesh.TriangleIndices.Add(x1 + y0);
                        mesh.TriangleIndices.Add(x1 + y1);
                        mesh.TriangleIndices.Add(x0 + y1);
                        
                    }
                }
            }
            int dir = Direction == BarDirection.Column ? 1 : -1;
            Tessellator.Tessellate(mesh, CreateVector(1, 0, 0), DiskTessellator.GetPoints(GetValueBounds(bounds).High, GetCategoryBounds(bounds), 
                bounds.Z, dir, tDiv, Direction));
            Tessellator.Tessellate(mesh, CreateVector(-1,0, 0), DiskTessellator.GetPoints(GetValueBounds(bounds).Low, GetCategoryBounds(bounds), 
                bounds.Z, -dir, tDiv, Direction));

            mesh.Freeze();
            return mesh;
        }
    }

}
